﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UMC.Net;

// using ;
using UMC.Web;

namespace UMC.ITME.Activities
{
    /// <summary>
    /// 应用日志
    /// </summary>
    [UMC.Web.Apiumc("ITME", "Log", Auth = WebAuthType.Admin)]
    public class SiteLogActivity : WebActivity
    {

        public override void ProcessActivity(WebRequest request, WebResponse response)
        {

            var Keys = this.AsyncDialog("Key", g =>
            {
                if (DateTime.TryParse(this.AsyncDialog("Date", r => $"{DateTime.Now:yyyy-MM-dd}"), out var date) == false)
                {
                    date = DateTime.Now;
                }
                var Days = Utility.IntParse(this.AsyncDialog("Days", "7"), 7);
                var type = (HttpLogerType)(Utility.IntParse(this.AsyncDialog("Type", "0"), 0) % 14);
                var Position = Utility.Parse(this.AsyncDialog("Position", "0"), 0L);

                var checkType = (int)Math.Pow(2, (int)type);

                var Keyword = (request.SendValues ?? request.Arguments)["Keyword"];
                ReadOnlySpan<byte> ip = ReadOnlySpan<byte>.Empty;
                ReadOnlySpan<byte> host = ReadOnlySpan<byte>.Empty;
                ReadOnlySpan<byte> user = ReadOnlySpan<byte>.Empty;
                ReadOnlySpan<byte> href = ReadOnlySpan<byte>.Empty;
                ReadOnlySpan<byte> path = ReadOnlySpan<byte>.Empty;
                var searchEntity = ReadOnlySpan<byte>.Empty;
                var ipStart = ReadOnlySpan<byte>.Empty;
                var ipEnd = ReadOnlySpan<byte>.Empty;
                int Status = 0;
                var isSearh = false;
                var Duration = 0;

                if (String.IsNullOrEmpty(Keyword) == false)
                {
                    var spanKey = Keyword.UTF8();
                    isSearh = true;
                    Status = Utility.IntParse(Keyword, 0);
                    if (Status < 100 || Status > 600)
                    {
                        Status = 0;
                        if (Keyword.EndsWith("ms") && (Duration = Utility.IntParse(Keyword.Substring(0, Keyword.Length - 2), 0)) > 0)
                        {
                            checkType |= (int)Math.Pow(2, (int)HttpLogerType.Duration);
                        }
                        else if (System.Net.IPAddress.TryParse(Keyword, out var ipAddress))
                        {
                            var address = new byte[16];
                            if (ipAddress.TryWriteBytes(address, out var _V))
                            {
                                ip = address.AsSpan(0, _V);
                            }

                            checkType |= (int)Math.Pow(2, (int)HttpLogerType.IP);
                        }

                        else if (Keyword.Contains(".*.") || Keyword.StartsWith("*.") || Keyword.EndsWith(".*"))
                        {
                            var ls = Keyword.Split('.');
                            var splitIndex = 0;
                            var values = new byte[4];
                            if (ls.Length == 4)
                            {
                                for (var i = 0; i < 4; i++)
                                {
                                    if (ls[i] == "*")
                                    {
                                        splitIndex = i;
                                    }
                                    else
                                    {
                                        values[i] = (byte)(Utility.IntParse(ls[i], 0) % 256);
                                    }
                                }
                                if (splitIndex > 0)
                                    ipStart = values.AsSpan(0, splitIndex);
                                if (splitIndex < 3)
                                    ipEnd = values.AsSpan(splitIndex + 1);

                                checkType |= (int)Math.Pow(2, (int)HttpLogerType.IP);
                            }
                        }
                        else if (Keyword.Contains(":*:") || Keyword.StartsWith("*:") || Keyword.EndsWith(":*"))
                        {
                            var ls = Keyword.Split(':');
                            var splitIndex = 0;
                            var values = new byte[4];
                            if (ls.Length == 8)
                            {
                                for (var i = 0; i < 4; i++)
                                {
                                    if (ls[i] == "*")
                                    {
                                        splitIndex = i;
                                    }
                                    else if (String.IsNullOrEmpty(ls[i]) == false)
                                    {
                                        if (Utility.TryParse(ls[i].UTF8(), 16, out int v))
                                        {
                                            values[i] = (byte)(v % 256);
                                        }
                                    }
                                }
                                if (splitIndex > 0)
                                    ipStart = values.AsSpan(0, splitIndex);
                                if (splitIndex < 7)
                                    ipEnd = values.AsSpan(splitIndex + 1);

                                checkType |= (int)Math.Pow(2, (int)HttpLogerType.IP);
                            }
                            else
                            {

                                searchEntity = Keyword.UTF8();
                            }

                        }
                        else if (Keyword.StartsWith("GET/") || Keyword.StartsWith("POST/") || Keyword.StartsWith("PUT/"))
                        {
                            path = Keyword.UTF8();

                            checkType |= (int)Math.Pow(2, (int)HttpLogerType.Path);
                        }
                        else if (Keyword.StartsWith("https://") || Keyword.StartsWith("http://"))
                        {
                            href = Keyword.UTF8();

                            checkType |= (int)Math.Pow(2, (int)HttpLogerType.Referer);
                        }
                        else if (Array.IndexOf(spanKey, (byte)'.') > 0 && Utility.IsHost(spanKey))
                        {
                            host = spanKey;
                            checkType |= (int)Math.Pow(2, (int)HttpLogerType.Host);
                        }
                        else if (Keyword.StartsWith(":"))
                        {

                            searchEntity = Keyword.Substring(1).UTF8();
                        }
                        else
                        {
                            checkType |= (int)Math.Pow(2, (int)HttpLogerType.Username);
                            user = Keyword.UTF8();
                        }
                    }

                }
                var sts = new List<WebMeta>();
                var bufer = System.Buffers.ArrayPool<byte>.Shared.Rent(NetLoger.MaxLogLength);
                try
                {
                    DateTime cdate = date;
                    int pfxDay = 0;
                    while (sts.Count < 200 && pfxDay < Days)
                    {
                        cdate = date.AddDays(0 - pfxDay);
                        var filename = UMC.Data.Utility.MapPath($"Logs\\{cdate:yy-MM-dd}.log");
                        if (System.IO.File.Exists(filename) == false)
                        {
                            pfxDay++;
                            continue;
                        }
                        var file = new FileInfo(filename);

                        if (Position == 0)
                        {
                            Position = file.Length;

                        }
                        var hash = new HashSet<String>();
                        using (var stream = file.OpenRead())
                        {
                            while (Position > 6)
                            {
                                stream.Position = Position - 6;
                                Position -= 6;
                                stream.Read(bufer, 0, 6);
                                if (bufer[5] == 0x03 && bufer[4] == 0x03)
                                {
                                    hash.Clear();
                                    var len = BitConverter.ToInt32(bufer);
                                    stream.Position = Position - len;
                                    var data = new WebMeta().Put("id", $"{cdate:yy-MM-dd}:{stream.Position - 6}");//.Put("date", date.ToShortDateString());
                                    var IsAppend = !isSearh;

                                    var IsType = false;
                                    while (stream.Position < Position)
                                    {
                                        int blen = stream.Read(bufer, 0, 2);

                                        HttpLogerType logType = (HttpLogerType)(byte)(bufer[0] >> 4);
                                        if (IsType == false)
                                        {
                                            IsType = logType == type;
                                        }
                                        var size = ((bufer[0] & 0x0f) << 8) | bufer[1];
                                        stream.Read(bufer, 0, size);
                                        if (size > len)
                                        {
                                            IsAppend = false;
                                            break;
                                        }
                                        var IsBreak = false;
                                        var value = bufer.AsSpan(0, size);
                                        switch (logType)
                                        {
                                            case HttpLogerType.Time:

                                                switch (value.Length)
                                                {
                                                    case 4:
                                                        data.Put("time", Utility.TimeSpan(BitConverter.ToUInt32(value)));
                                                        break;
                                                    case 8:
                                                        data.Put("time", Utility.TimeSpan(BitConverter.ToUInt32(value)));
                                                        if (type > 0)
                                                        {
                                                            if ((BitConverter.ToInt32(value.Slice(4)) & checkType) == 0)
                                                            {
                                                                IsAppend = false;
                                                                IsBreak = true;
                                                            }

                                                        }
                                                        break;
                                                }
                                                break;
                                            case HttpLogerType.IP:
                                                if (ip.Length > 0)
                                                {
                                                    if (value.SequenceEqual(ip))
                                                    {
                                                        IsAppend = true;
                                                    }
                                                    else
                                                    {
                                                        IsBreak = true;
                                                    }

                                                }
                                                else if (ipEnd.IsEmpty == false || ipStart.IsEmpty == false)
                                                {
                                                    if (ipEnd.IsEmpty == false & ipStart.IsEmpty == false)
                                                    {
                                                        if (value.StartsWith(ipStart) && value.EndsWith(ipEnd))
                                                        {
                                                            IsAppend = true;
                                                        }
                                                        else
                                                        {
                                                            IsBreak = true;
                                                        }
                                                    }
                                                    else if (ipEnd.IsEmpty == false)
                                                    {
                                                        if (value.EndsWith(ipEnd))
                                                        {
                                                            IsAppend = true;
                                                        }
                                                        else
                                                        {
                                                            IsBreak = true;
                                                        }
                                                    }
                                                    else
                                                    {
                                                        if (value.StartsWith(ipStart))
                                                        {
                                                            IsAppend = true;
                                                        }
                                                        else
                                                        {
                                                            IsBreak = true;
                                                        }
                                                    }
                                                }
                                                try
                                                {
                                                    data.Put("IP", new System.Net.IPAddress(value).ToString());
                                                }
                                                catch
                                                {

                                                    data.Put("IP", value.UTF8());
                                                }
                                                break;
                                            case HttpLogerType.Host:
                                                if (host.IsEmpty == false)
                                                {
                                                    if (value.SequenceEqual(host))
                                                    {
                                                        IsAppend = true;
                                                        data.Put("host", value.UTF8());
                                                    }
                                                    else
                                                    {
                                                        IsBreak = true;
                                                    }

                                                    break;
                                                }
                                                data.Put("host", value.UTF8());
                                                goto default;
                                            case HttpLogerType.UserAgent:
                                                data.Put("userAgent", value.UTF8());
                                                goto default;
                                            case HttpLogerType.Path:
                                                if (path.IsEmpty == false)
                                                {
                                                    if (value.StartsWith(path))
                                                    {
                                                        IsAppend = true;
                                                        data.Put("path", value.UTF8());
                                                    }
                                                    else
                                                    {
                                                        IsBreak = true;
                                                    }
                                                    break;
                                                }
                                                data.Put("path", value.UTF8());
                                                goto default;
                                            case HttpLogerType.Referer:
                                                if (href.IsEmpty == false)
                                                {
                                                    if (value.StartsWith(href))
                                                    {
                                                        IsAppend = true;
                                                        data.Put("referer", value.UTF8());
                                                    }
                                                    else
                                                    {
                                                        IsBreak = true;
                                                    }
                                                    break;
                                                }
                                                data.Put("referer", value.UTF8());
                                                goto default;
                                            case HttpLogerType.Username:
                                                if (user.IsEmpty == false)
                                                {
                                                    if (value.SequenceEqual(user))
                                                    {
                                                        IsAppend = true;
                                                        data.Put("user", value.UTF8());
                                                    }
                                                    else
                                                    {
                                                        IsBreak = true;
                                                    }
                                                    break;
                                                }
                                                data.Put("user", value.UTF8());
                                                goto default;
                                            case HttpLogerType.Status:
                                                if (value.Length == 4)
                                                {
                                                    var st = BitConverter.ToInt32(value);
                                                    if (IsAppend == false && Status > 0)
                                                    {
                                                        if (Status == st)
                                                        {
                                                            IsAppend = true;
                                                        }
                                                        else
                                                        {
                                                            IsBreak = true;
                                                        }

                                                    }
                                                    data.Put("status", st);
                                                }
                                                break;
                                            case HttpLogerType.Duration:
                                                if (value.Length == 4)
                                                {
                                                    var dur = BitConverter.ToInt32(value);
                                                    if (IsAppend == false && Duration > 0)
                                                    {
                                                        if (dur >= Duration)
                                                        {
                                                            IsAppend = true;
                                                        }
                                                        else
                                                        {
                                                            IsBreak = true;
                                                        }
                                                    }
                                                    data.Put("duration", $"{dur}ms");

                                                }
                                                break;
                                            case HttpLogerType.Info:

                                                hash.Add("Info");
                                                goto default;
                                            case HttpLogerType.Alert:

                                                hash.Add("Alert");
                                                goto default;
                                            case HttpLogerType.Debug:

                                                hash.Add("Debug");
                                                goto default;
                                            case HttpLogerType.Error:
                                                hash.Add("Error");
                                                goto default;
                                            default:
                                                if (IsAppend == false && searchEntity.Length > 0)
                                                {
                                                    if (value.Contains(searchEntity))
                                                    {
                                                        IsAppend = true;

                                                    }
                                                }
                                                break;
                                        }
                                        if (IsBreak)
                                        {
                                            break;
                                        }
                                    }
                                    if (IsAppend && IsType && data.Count > 1)
                                    {
                                        if (hash.Count > 0)
                                        {
                                            data.Put("type", hash.ToArray());
                                        }
                                        sts.Add(data);
                                    }
                                    Position = Position - len - 6;
                                    if (sts.Count > 200)
                                    {
                                        break;
                                    }


                                }
                                else
                                {
                                    break;
                                }
                            }
                        }

                        pfxDay++;
                    }
                    var rdata = new WebMeta().Put("data", sts);

                    if (pfxDay < Days)
                    {
                        rdata.Put("Date", cdate.ToString("yyyy-MM-dd")).Put("Days", Days - pfxDay).Put("Position", Position);

                    }
                    response.Redirect(request.IsMaster ? rdata.Put("IsMaster", true) : rdata);
                }
                finally
                {
                    System.Buffers.ArrayPool<byte>.Shared.Return(bufer);
                }
                return this.DialogValue("none");
            }).Split(':');

            var Position = Utility.Parse(Keys[1], 0L);
            var filename = UMC.Data.Utility.MapPath($"Logs\\{Keys[0]}.log");
            if (System.IO.File.Exists(filename) == false)
            {
                this.Prompt("参数错误");
            }
            var buffer = System.Buffers.ArrayPool<byte>.Shared.Rent(NetLoger.MaxLogLength);
            try
            {
                var ui2 = UISection.Create(new UITitle("日志详情"));
                var ui = ui2.NewSection();
                using (var stream = File.OpenRead(filename))
                {
                    stream.Position = Position;
                    stream.Read(buffer, 0, 6);
                    StringValue IP = StringValue.Empty;
                    StringValue Fingerprint = StringValue.Empty;
                    if (buffer[0] == 0x02 && buffer[1] == 0x02)
                    {
                        var len = BitConverter.ToInt32(buffer.AsSpan(2, 4));
                        var maxPostn = len + Position + 6;

                        var sbReq = new StringBuilder();
                        var sbInfo = new StringBuilder();
                        var sbDebug = new StringBuilder();
                        var sbAlert = new StringBuilder();
                        var sbError = new StringBuilder();


                        while (stream.Position < maxPostn)
                        {
                            int blen = stream.Read(buffer, 0, 2);

                            HttpLogerType logType = (HttpLogerType)(byte)(buffer[0] >> 4);

                            var size = ((buffer[0] & 0x0f) << 8) | buffer[1];
                            if (size == 0)
                            {
                                continue;
                            }
                            if (size > len)
                            {
                                break;
                            }
                            stream.Read(buffer, 0, size);

                            var value = buffer.AsSpan(0, size);
                            switch (logType)
                            {
                                case HttpLogerType.Time:
                                    ui.AddCell("请求时间", Utility.TimeSpan(BitConverter.ToUInt32(value)).ToLongTimeString());
                                    break;
                                case HttpLogerType.Fingerprint:
                                    {
                                        Fingerprint = new StringValue(value.ToArray());
                                        var frie = DataFactory.Instance().Firewall(new Net.StringValue(buffer, 0, size));
                                        var finder = Utility.ToBase64String(value);
                                        switch (frie?.AuthType)
                                        {
                                            default:
                                            case Entities.AuthType.Check:
                                                var check = UICell.UI('\uea05', 0xffbe00, "应用指纹", finder, new UIClick(request.Model, "Firewall", finder));
                                                ui2.Add(check);
                                                break;
                                            case Entities.AuthType.Allow:
                                                var allow = UICell.UI('\uea04', 0x09bb07, "应用指纹", finder, new UIClick(request.Model, "Firewall", finder));
                                                ui2.Add(allow);
                                                break;
                                            case Entities.AuthType.Deny:
                                                var deny = UICell.UI('\uea04', 0xff4949, "应用指纹", finder, new UIClick(request.Model, "Firewall", finder));

                                                ui2.Add(deny);
                                                break;
                                        }
                                    }
                                    break;
                                case HttpLogerType.IP:
                                    {
                                        IP = new StringValue(value.ToArray());
                                        var frie = DataFactory.Instance().Firewall(IP);
                                        var finder = new System.Net.IPAddress(value).ToString();


                                        // var provider = Data.WebResource.Instance().Provider;

                                        // var secret = provider["appSecret"];
                                        // var webr = new Uri(APIProxy.Uri, "Transfer").WebRequest();
                                        // var item = new WebMeta().Put("type", "IP", "value", finder);

                                        // UMC.ITME.Utility.Sign(webr, secret);
                                        // var xhv = UMC.Data.JSON.Deserialize<WebMeta>(webr.Post(item, out var xhr));
                                        // if (xhr?.StatusCode == System.Net.HttpStatusCode.OK)
                                        // {
                                        //     ui2.AddCell('\uf277', "请求地", $"{xhv["country"]} {xhv["province"]} {xhv["city"]}");
                                        // }
                                        switch (frie?.AuthType)
                                        {
                                            default:
                                            case Entities.AuthType.Check:
                                                var check = UICell.UI('\uea05', 0xffbe00, "终端IP", finder, new UIClick(request.Model, "Firewall", finder));
                                                ui2.Add(check);
                                                break;
                                            case Entities.AuthType.Allow:
                                                var allow = UICell.UI('\uea04', 0x09bb07, "终端IP", finder, new UIClick(request.Model, "Firewall", finder));
                                                ui2.Add(allow);
                                                break;
                                            case Entities.AuthType.Deny:
                                                var deny = UICell.UI('\uea04', 0xff4949, "终端IP", finder, new UIClick(request.Model, "Firewall", finder));
                                                ui2.Add(deny);
                                                break;
                                        }


                                    }
                                    break;
                                case HttpLogerType.Host:
                                    sbReq.Append($"Host:{value.UTF8()}\r\n");
                                    break;
                                case HttpLogerType.UserAgent:
                                    sbReq.Append($"User-Agent:{value.UTF8()}\r\n");
                                    break;
                                case HttpLogerType.Path:
                                    sbReq.Insert(0, $"{value.UTF8()}\r\n");
                                    break;
                                case HttpLogerType.Referer:
                                    sbReq.Append($"Referer:{value.UTF8()}\r\n");
                                    break;
                                case HttpLogerType.Username:
                                    ui.AddCell("请求用户", value.UTF8());
                                    break;
                                case HttpLogerType.Status:
                                    ui.AddCell("响应状态", BitConverter.ToInt32(value).ToString());
                                    break;
                                case HttpLogerType.Body:
                                    sbReq.AppendLine();
                                    sbReq.AppendLine(value.UTF8());
                                    sbReq.AppendLine();

                                    break;
                                case HttpLogerType.Duration:
                                    ui.AddCell("响应耗时", $"{BitConverter.ToInt32(value)}ms");
                                    break;
                                case HttpLogerType.Info:
                                    sbInfo.AppendLine(value.UTF8());
                                    break;
                                case HttpLogerType.Debug:
                                    sbDebug.AppendLine(value.UTF8());
                                    break;
                                case HttpLogerType.Alert:
                                    sbAlert.AppendLine(value.UTF8());
                                    break;
                                case HttpLogerType.Error:
                                    sbError.AppendLine(value.UTF8());
                                    break;
                                default:

                                    break;
                            }
                        }
                        if (IP.IsEmpty == false && Fingerprint.IsEmpty == false)
                        {
                            var clientInfo = HttpsMimeSocket.ClientCache.Get(new HttpsMimeSocket.ClientInfo { IP = IP, Finger = Fingerprint });
                            if (clientInfo != null)
                            {
                                var error = BitConverter.ToInt32(clientInfo.Error.Span);
                                var clientId = $"{Utility.ToBase64String(IP.Span)}.{Utility.ToBase64String(Fingerprint.Span)}";
                                ui.NewSection().AddCell("终端连接", $"{BitConverter.ToInt32(clientInfo.Quantity.Span)}条", new UIClick("Http", "Client", "__", $"{clientId}/Stop"))
                                .AddCell("终端攻击", $"{error}次", new UIClick("Http", "Client", "__", error > 0 ? clientId : $"{clientId}/Pause"));
                            }


                        }
                        if (sbReq.Length > 0)
                        {
                            sbReq.Insert(0, "```\r\n");
                            sbReq.AppendLine();
                            sbReq.AppendLine("```");
                            var rui = ui.NewSection();
                            rui.Header.Put("text", "请求");
                            rui.AddCells(UMC.Data.Markdown.Transform(sbReq.ToString()));
                            sbReq.Clear();
                        }
                        if (sbInfo.Length > 0)
                        {
                            sbInfo.Insert(0, "```\r\n");
                            sbInfo.AppendLine();
                            sbInfo.AppendLine("```");
                            var rui = ui.NewSection();
                            rui.Header.Put("text", "信息");
                            rui.AddCells(UMC.Data.Markdown.Transform(sbInfo.ToString()));
                        }

                        if (sbDebug.Length > 0)
                        {

                            sbDebug.Insert(0, "```\r\n");
                            sbDebug.AppendLine();
                            sbDebug.AppendLine("```");
                            var rui = ui.NewSection();
                            rui.Header.Put("text", "调试");
                            rui.AddCells(UMC.Data.Markdown.Transform(sbDebug.ToString()));
                        }
                        if (sbAlert.Length > 0)
                        {

                            sbAlert.Insert(0, "```\r\n");
                            sbAlert.AppendLine();
                            sbAlert.AppendLine("```");
                            var rui = ui.NewSection();
                            rui.Header.Put("text", "警告");
                            rui.AddCells(UMC.Data.Markdown.Transform(sbAlert.ToString()));

                        }
                        if (sbError.Length > 0)
                        {
                            sbError.Insert(0, "```\r\n");
                            sbError.AppendLine();
                            sbError.AppendLine("```");
                            var rui = ui.NewSection();
                            rui.Header.Put("text", "错误");
                            rui.AddCells(UMC.Data.Markdown.Transform(sbError.ToString()));
                        }

                    }
                }
                ui2.SendTo(this.Context);
            }
            finally
            {

                System.Buffers.ArrayPool<byte>.Shared.Return(buffer);
            }
        }
    }
}